﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using Org.Apache.Zookeeper.Data;
using ZooKeeperNet;

namespace ZooKeeperManage
{
    /// <summary>
    /// zookeeper 服务
    /// </summary>
    public class ZooKeeperService : IDisposable
    {
        private readonly ZooKeeper _zk;
        /// <summary>
        /// 数据变更
        /// </summary>
        public event Action<string> DataChanged;
        /// <summary>
        /// 子节点变更
        /// </summary>
        public event Action<List<string>> ChildrenChanged;
        /// <summary>
        /// 节点创建
        /// </summary>
        public event Action<string> NodeCreated;
        /// <summary>
        /// 节点删除
        /// </summary>
        public event Action<string> NodeDeleted;

        private readonly Watcher _watcher;

        public ZooKeeperService(string connectString = "127.0.0.1:2181", int timeout = 5000)
        {
            if (_zk == null)
            {
                _watcher = new Watcher();
                _watcher.ChildrenChanged += temp =>
                {
                    var children = _zk.GetChildren(temp, true);
                    ChildrenChanged?.Invoke(children.ToList());
                };
                _watcher.NodeCreated += temp =>
                {
                   _zk.GetChildren(temp, true);
                    NodeCreated?.Invoke(temp);
                };
                _watcher.NodeDeleted += temp =>
                {
                    _zk.Exists(temp, true);
                    NodeDeleted?.Invoke(temp);
                };
                //创建一个Zookeeper实例，第一个参数为目标服务器地址和端口，第二个参数为Session超时时间，第三个为节点变化时的回调方法
                _zk = new ZooKeeper(connectString, new TimeSpan(0, 0, 0, timeout), _watcher);
            }
        }


        public bool SubscribeDataChanged(string path)
        {
            _watcher.DataChanged += temp =>
            {
                byte[] tempArray = _zk.GetData(temp, true, null);
                DataChanged?.Invoke(Encoding.ASCII.GetString(tempArray));
            };
            _zk.GetData(path, true, null);
            return true;
        }
        public bool SubscribeChildrenChanged(string path)
        {
            return SubscribeNode(path);
        }
        public bool SubscribeNodeCreated(string path)
        {
            return SubscribeNode(path);
        }
        public bool SubscribeNodeDeleted(string path)
        {
            return SubscribeNode(path);
        }
        /// <summary>
        /// 注册节点事件
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        private bool SubscribeNode(string path)
        {
            try
            {
                _zk.GetChildren(path, true);
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        //OPEN_ACL_UNSAFE;//完全开放
        //CREATOR_ALL_ACL;//建该ZNode的连接拥有所有权限
        //READ_ACL_UNSAFE;//所有的客户端都可读


        //Ephemeral;//临时节点（连接断开自动删除）
        //EphemeralSequential;//临时有序节点（连接断开自动删除）
        //Persistent;//持久化节点
        //PersistentSequential;//持久化有序节点
        public CreateResultEnum Create(string path, byte[] data, List<ACL> ids, CreateMode createMode)
        {
            string nodeId = "";
            return Create(path, data, ids, createMode, ref nodeId);
        }
        public CreateResultEnum Create(string path, byte[] data, List<ACL> ids, CreateMode createMode, ref string nodeId)
        {
            try
            {
                string[] pathArray = path.Split('/');
                //1.判断上级节点是否存在，不存在创建
                string parent = "";
                for (int i = 1; i < pathArray.Length - 1; i++)
                {
                    parent += "/" + pathArray[i];
                    if (_zk.Exists(parent, true) == null) //判断上级目录是否存在
                        _zk.Create(parent, parent.GetBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.Persistent);
                }
                //2.创建节点
                // if (_zk.Exists(path, true) == null)
                nodeId = _zk.Create(path, data, ids, createMode);

                return CreateResultEnum.Success;

            }
            catch (KeeperException.ConnectionLossException)
            {
                return CreateResultEnum.ConnectionError;//连接错误
            }
            catch (KeeperException.NoNodeException)
            {
                return CreateResultEnum.NoNode; //没有节点
            }
            catch (KeeperException.NodeExistsException)
            {
                return CreateResultEnum.NodeExists; //节点存在
            }
            catch (Exception ex)
            {
                if (ex.Message.Equals("Path must start with / character")) return CreateResultEnum.PathStart;   //路径必须以'/'开头

                return CreateResultEnum.Fail;
            }

        }

        public List<string> GetChildren(string path)
        {
            return _zk.GetChildren(path, true).ToList();
        }
        public byte[] GetData(string path)
        {
            return _zk.GetData(path, true, null);
        }
        public bool Delete(string path)
        {
            try
            {
                _zk.Delete(path, -1);
                return true;
            }
            catch (Exception)
            {

                return false;
            }
        }
        public void Dispose()
        {
            _zk.Dispose();
        }


    }
    public enum CreateResultEnum
    {
        [Description("成功")]
        Success = 1,
        [Description("失败")]
        Fail,
        [Description("连接错误")]
        ConnectionError,
        [Description("没有节点")]
        NoNode,
        [Description("节点存在")]
        NodeExists,
        [Description("路径必须以'/'开头")]
        PathStart
    }
    public class Watcher : IWatcher
    {
        /// <summary>
        /// 数据变更
        /// </summary>
        public event Action<string> DataChanged;
        /// <summary>
        /// 子节点变更
        /// </summary>
        public event Action<string> ChildrenChanged;
        /// <summary>
        /// 节点创建
        /// </summary>
        public event Action<string> NodeCreated;
        /// <summary>
        /// 节点创建
        /// </summary>
        public event Action<string> NodeDeleted;

        public void Process(WatchedEvent @event)
        {
            switch (@event.Type)
            {
                case EventType.None:
                    break;
                case EventType.NodeCreated:
                    NodeCreated?.Invoke(@event.Path);
                    break;
                case EventType.NodeDeleted:
                    NodeDeleted?.Invoke(@event.Path);
                    break;
                case EventType.NodeDataChanged:
                    DataChanged?.Invoke(@event.Path);
                    break;
                case EventType.NodeChildrenChanged:
                    ChildrenChanged?.Invoke(@event.Path);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }
    }
}
